//----------------------------------------------------------------------------
//  ,-.
// /_\___ ,   Ninja Framework
// '  ___\    (C) Riccardo Capra - http://riccar.do
//  //   \\
//----------------------------------------------------------------------------
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------------

#ifndef NJ_SMART_PTR_H
#	define NJ_SMART_PTR_H

#	include "NjNinjaSyncLib.h"
#	include "NjFundamentalMacros.h"
#	include "NjFundamentalTypes.h"

	/// \brief A pointer to reference counter objects
	///
	/// It automatically increments the counter of the pointed object when starts using it and decrements the counter of the pointed object when ends using it
	/// \param tType is a type implementing IncrementReference, DecrementReference and destructor methods
	template<class tType>
	class NjSmartPtr
	{
	public:
		/// \brief The underlying type referred by this smart pointer
		typedef tType Type;

		/// \brief Creates a new smart pointer pointing to NJ_NULL
		NjSmartPtr();

		/// \brief Creates a new smart pointer pointing to /em pPtr
		NjSmartPtr(Type * pPtr);

		/// \brief Creates a new smart pointer pointing to /em kSmartPtr
		NjSmartPtr(NjSmartPtr const & kSmartPtr);

		/// \brief Destroys the smart pointer
		~NjSmartPtr();

		/// \brief Sets the pointed object to \em pPtr
		NjSmartPtr & operator=(Type * pPtr);

		/// \brief Sets the pointed object to \em kSmartPtr
		NjSmartPtr & operator=(NjSmartPtr const & kSmartPtr);

		/// \brief Returns NJ_TRUE if the pointed object is \em pPtr
		NjBool operator==(Type * pPtr) const;

		/// \brief Returns NJ_TRUE if the pointed object is \em kSmartPtr
		NjBool operator==(NjSmartPtr const & kSmartPtr) const;

		/// \brief Returns NJ_FALSE if the pointed object is \em pPtr
		NjBool operator!=(Type * pPtr) const;

		/// \brief Returns NJ_FALSE if the pointed object is \em kSmartPtr
		NjBool operator!=(NjSmartPtr const & kSmartPtr) const;

		/// \copydoc IsNull
		NjBool operator!() const;

		/// \brief Returns NJ_TRUE if the pointed object is NJ_NULL
		NjBool IsNull() const;

		/// \brief Returns a reference to the pointed object
		/// 
		/// If the pointed object is NJ_NULL an assertion is triggered
		Type & operator*() const;

		/// \brief Access to a member of the pointed object
		/// 
		/// If the pointed object is NJ_NULL an assertion is triggered
		Type * operator->() const;

		/// \brief Returns the pointed object
		///
		/// If the pointed object is NJ_NULL it will return NJ_NULL
		operator Type * () const;

		/// \brief Swaps the content of two smart pointers
		void Swap(NjSmartPtr & kOther);

	private:
		/// \brief If \em pPtr is not NJ_NULL it will increment it
		static void IncrementReference(Type * pPtr);

		/// \brief If \em pPtr is not NJ_NULL it will decrement it
		static void DecrementReference(Type * pPtr);

		/// \brief The pointed object
		Type * m_pPtr;
	};

/// \brief Creates a type reppresenting the smart pointer to type \em tType
#	define NJ_DECLARE_SMART_PTR(tType)	\
		class tType;					\
		typedef NjSmartPtr<tType> NJ_CONCATENATE(tType, Ptr);

#	include "NjAssert.h"
#	include "NjSmartPtr_inline.h"

#endif // NJ_SMART_PTR_H
